Release 10.1A: OpenEdge Development:
Progress Dynamics Advanced Development
Organization of the manager code
The managers are written to allow a single body of code to be compiled for use on both the server and the client, with the portions requiring database access segregated to the server. For example, when using the Profile Manager, remember that a basic principle of the managers is that there is a server version of the manager that handles all Repository database access, and a client version that other procedures in the client communicate with. The code that is common to both client and server versions is placed in an include file whose name is of the form afxxxmngrp.i, where xxx represents a three-letter abbreviation of the manager name, such as:
These include files are in the
af/appdirectory. Theafdirectory tree is where code related to supporting the framework itself is located (‘af’ stands for ‘application framework’). Theappsubdirectory is where code goes that runs on the AppServer in a distributed environment.Two other procedures include this file:
- The client version of the manager is
af/sup2/afxxxclntp.p. Thesup2directory is where general support code for the application framework goes. The2insup2, as in other framework directories such ascod2, indicates that this is code specific to the Version 9, or ADM2 version of the framework, as opposed to earlier code with its origins in Version 8.- The server version of the manager is
af/app/afxxxsrvrp.p. Again, this is in theaf/app directorybecause it is classified asAppServer Code. Each of these two procedures basically just defines a preprocessor and then includes af/app/afxxxmngrp.i.For example, this is the only code inafproclntp.p:
This is the only code in
afprosrvrp.p:
These two preprocessors, client-side and server-side, allow the procedures in the common include file to separate out blocks of code where needed that should be compiled only on the server side of the manager or only on the client side, as we’ll see in a moment.
The code that must be separated out is largely code that accesses the Repository database. This code must be executed only on the server. Where the client needs access to the same data, it must run the same code on the AppServer.
The most efficient way to structure the manager on the server is to have all of the code, including the procedures that access the database, together in one persistent procedure where it can all be prestarted and therefore running whenever it is needed. On the other hand, when the client code must access the same database procedures, the most efficient way to do that is to make a single call to an external procedure (Progress
.rfile) on the server, which takesINPUTparameters and returns whatever the result is asOUTPUTparameters. Making a single call like this to a stateless AppServer session does not bind the client to the server, and gets the results in a single AppServer call. By contrast, running an internal procedure inside a larger.rfile requires making a call to establish the connection and run the .r file as a persistent procedure, then a separate call to run an internal procedure entry point inside it, and then another call to delete the server procedure. The client is bound to the AppServer session for the duration of this process.To improve functionality, the managers use a technique of isolating database access code in external
.pfiles to be run from the client, and then including those in a version of the same code that runs on the server.The common include file,
afpromngrp.i, has code in its main block that defines an internal procedure with the same name as each external procedure that contains server-side-only code. Only if the server-side preprocessor is defined, these definitions are compiled into the procedure that includes the common code, as shown:
In this way the version of the manager that runs on the server is a single large persistent procedure that incorporates all the database access code as well as the common code. This is an efficient way to operate because the managers are pre-started and left running for the duration of the AppServer session. Note that this version of the manager is also the one that runs if you are running locally, without an AppServer.
Each of the
.pfiles is also compiled as a separate unit, so that its code can be run from a client session. Because each of these must be a simple procedure call, not a call to an internal procedure inside some larger file, all the executable code for these data access procedures must be in the main block, the part of the file that is executed when the procedure runs, as shown in Figure 6–2.Figure 6–2: Executable code in main block
![]()
From a client session, the data access procedures are run as individual
.rfiles on the AppServer, as shown in Figure 6–3.Figure 6–3: Data access file
![]()
Within the server session itself, or when there is no separate AppServer, the data access code is compiled right into the manager itself.
For example, here is an excerpt of the code in the main block of the first support procedure for the Profile Manager,
afbldclicp.p, which builds the temp-table cache of profile values for a user and returns it to the session where that user is running:
Note that the procedure can take whatever input and output parameters it needs. This one takes a list of profile type codes as input and returns the resulting temp-table of all values of those types for the current user. This is then kept on the client to be used during the session. In some cases, you might find the parameters in the Definitions section of the procedure rather than in the main block. This is just an organizational choice and doesn’t affect how the procedure compiles.
Because this is code from one of the managers itself, it makes direct references to the Repository database tables. In code that you write in your application, including custom managers you create, you should use the available API for the manager, along with general-purpose routines, such as
getEntityDescription, to get data from the Repository without direct reference to table and field names. The APIs are supported and kept compatible in the future; the specifics of the underlying table structure might be subject to change in future releases of the product.Because this code is loading a temp-table of data for the client, it should not be run on the remote part of a distributed manager, but only in the client part of a distributed manager, or in a manager that is being run without AppServer. Thus, if you look at the main block of the common code include file, you see a reference that is found frequently in the manager code, as shown:
Checking the
SESSIONobject tells the code whether it has been started on an AppServer session or WebSpeed agent. If this is not the case, then the code runs the internal procedurebuildClientCache. This code is in the main block of the include file, so it is run as soon as the manager is first executed on session startup.Looking next at
buildClientCacheitself, you can see an example of how the internal versus external partitioning is used in the code, as shown:
First it checks the
REMOTEparameter as before. Then it starts by emptying theProfile Datatemp-table in case there is any leftover data in it. This could be the case if the session is restarted.Next comes the code block of interest. What it says, in effect, is if this is the server-side version of the manager, compile in a
RUNstatement to run the cache-loading procedureafbldclicpas an internal procedure within the manager. Otherwise compile in a statement to run it as an external procedure on the default AppServer handle.This is not a coding style to follow in new code that you write, because new features in the 4GL allow you to achieve the same flexibility without this structure. But within the existing managers, it works effectively.
|
Copyright © 2005 Progress Software Corporation www.progress.com Voice: (781) 280-4000 Fax: (781) 280-4095 |